package com.demandbreakdown.xiyun.common.aspect;

import cn.hutool.core.util.StrUtil;
import com.demandbreakdown.xiyun.common.annotation.OperationLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 操作日志切面
 *
 * @author poxz
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    /**
     * SpEL表达式解析器
     */
    private final ExpressionParser parser = new SpelExpressionParser();

    /**
     * 参数名发现器
     */
    private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();

    /**
     * 定义切点：所有标注了@OperationLog注解的方法
     */
    @Pointcut("@annotation(com.demandbreakdown.xiyun.common.annotation.OperationLog)")
    public void operationLogPointcut() {
    }

    /**
     * 环绕通知：记录操作日志
     */
    @Around("operationLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.nanoTime();

        // 获取方法签名和注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);

        try {
            // 执行目标方法
            Object result = joinPoint.proceed();

            // 记录成功日志
            logSuccess(joinPoint, method, operationLog, result, startTime);

            return result;
        } catch (Throwable e) {
            // 记录失败日志
            logError(joinPoint, method, operationLog, e, startTime);
            throw e;
        }
    }

    /**
     * 记录成功日志
     */
    private void logSuccess(ProceedingJoinPoint joinPoint, Method method, OperationLog operationLog,
                            Object result, long startTime) {
        try {
            long costTime = (System.nanoTime() - startTime) / 1_000_000; // 转为毫秒

            StringBuilder logMsg = new StringBuilder();
            logMsg.append(operationLog.value()).append("成功");

            // 添加业务类型
            if (StrUtil.isNotBlank(operationLog.bizType())) {
                logMsg.append(" [").append(operationLog.bizType()).append("]");
            }

            // 提取参数
            Map<String, Object> params = extractParams(joinPoint, method, operationLog);
            if (!params.isEmpty()) {
                logMsg.append(": ");
                params.forEach((key, value) -> logMsg.append(key).append("=").append(value).append(", "));
                // 删除最后的逗号和空格
                logMsg.setLength(logMsg.length() - 2);
            }

            // 添加耗时
            logMsg.append(" [耗时: ").append(costTime).append("ms]");

            // 添加返回值（如果需要）
            if (operationLog.recordResult() && result != null) {
                logMsg.append(", [返回值: ").append(result.toString()).append("]");
            }

            log.info(logMsg.toString());
        } catch (Exception e) {
            log.warn("记录操作日志失败: {}", e.getMessage());
        }
    }

    /**
     * 记录失败日志
     */
    private void logError(ProceedingJoinPoint joinPoint, Method method, OperationLog operationLog,
                          Throwable throwable, long startTime) {
        try {
            long costTime = (System.nanoTime() - startTime) / 1_000_000; // 转为毫秒

            StringBuilder logMsg = new StringBuilder();
            logMsg.append(operationLog.value()).append("失败");

            // 添加业务类型
            if (StrUtil.isNotBlank(operationLog.bizType())) {
                logMsg.append(" [").append(operationLog.bizType()).append("]");
            }

            // 提取参数
            Map<String, Object> params = extractParams(joinPoint, method, operationLog);
            if (!params.isEmpty()) {
                logMsg.append(": ");
                params.forEach((key, value) -> logMsg.append(key).append("=").append(value).append(", "));
                // 删除最后的逗号和空格
                logMsg.setLength(logMsg.length() - 2);
            }

            // 添加耗时和错误信息
            logMsg.append(" [耗时: ").append(costTime).append("ms]");
            logMsg.append(", [错误: ").append(throwable.getClass().getSimpleName())
                    .append(" - ").append(throwable.getMessage()).append("]");

            log.error(logMsg.toString());
        } catch (Exception e) {
            log.warn("记录操作日志失败: {}", e.getMessage());
        }
    }

    /**
     * 提取参数
     * 支持SpEL表达式和自动提取
     */
    private Map<String, Object> extractParams(ProceedingJoinPoint joinPoint, Method method,
                                              OperationLog operationLog) {
        Map<String, Object> params = new LinkedHashMap<>();

        try {
            // 获取方法参数名和参数值
            String[] parameterNames = discoverer.getParameterNames(method);
            Object[] args = joinPoint.getArgs();

            if (parameterNames == null || parameterNames.length == 0) {
                return params;
            }

            // 如果指定了params（SpEL表达式），使用SpEL提取
            if (operationLog.params().length > 0) {
                extractParamsBySpEL(operationLog.params(), parameterNames, args, params);
            } else if (operationLog.recordParams()) {
                // 否则，如果recordParams为true，自动提取基本类型参数
                extractParamsAuto(parameterNames, args, params);
            } else {
                // 默认：智能提取常见的ID参数
                extractParamsSmart(parameterNames, args, params);
            }

        } catch (Exception e) {
            log.warn("提取参数失败: {}", e.getMessage());
        }

        return params;
    }

    /**
     * 通过SpEL表达式提取参数
     */
    private void extractParamsBySpEL(String[] spelExpressions, String[] parameterNames,
                                     Object[] args, Map<String, Object> params) {
        // 创建SpEL上下文
        EvaluationContext context = new StandardEvaluationContext();

        // 将方法参数放入上下文
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }

        // 解析SpEL表达式
        for (String spelExpression : spelExpressions) {
            try {
                Expression expression = parser.parseExpression(spelExpression);
                Object value = expression.getValue(context);

                // 提取参数名（去掉#和.后面的部分）
                String paramName = spelExpression.replace("#", "");
                if (paramName.contains(".")) {
                    paramName = paramName.substring(paramName.lastIndexOf(".") + 1);
                }

                if (value != null) {
                    params.put(paramName, value);
                }
            } catch (Exception e) {
                log.warn("SpEL表达式解析失败: {}, 错误: {}", spelExpression, e.getMessage());
            }
        }
    }

    /**
     * 自动提取所有参数
     */
    private void extractParamsAuto(String[] parameterNames, Object[] args, Map<String, Object> params) {
        for (int i = 0; i < parameterNames.length; i++) {
            if (args[i] == null) {
                continue;
            }

            // 只记录基本类型和字符串，避免复杂对象
            if (isSimpleType(args[i])) {
                params.put(parameterNames[i], args[i]);
            }
        }
    }

    /**
     * 智能提取常见的业务参数（默认模式）
     */
    private void extractParamsSmart(String[] parameterNames, Object[] args, Map<String, Object> params) {
        for (int i = 0; i < parameterNames.length; i++) {
            if (args[i] == null) {
                continue;
            }

            String paramName = parameterNames[i];

            // 提取常见的ID参数
            if (paramName.contains("Id") || paramName.contains("id")) {
                if (isSimpleType(args[i])) {
                    params.put(paramName, args[i]);
                }
            }
            // 提取常见的名称参数
            else if (paramName.contains("Name") || paramName.contains("name")) {
                if (args[i] instanceof String) {
                    params.put(paramName, args[i]);
                }
            }
        }
    }

    /**
     * 判断是否为简单类型
     */
    private boolean isSimpleType(Object obj) {
        return obj instanceof Number
                || obj instanceof CharSequence
                || obj instanceof Boolean
                || obj instanceof Character
                || obj.getClass().isPrimitive();
    }
}
